<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');

if (! class_exists('PrunusTable')) {
  /**
   * PrunusTable
   * 
   * Standard Prunus Table interface
   * 
   * @package Prunus   
   * @author agosto <agosto@7dedos.com>
   * @copyright	Copyright (c) 2009-2010, 7dedos
   * @link http://www.7dedos.com
   * @version 0.1
   * @access public
   */
  abstract class PrunusTable extends Model {
    protected $tableName;
    protected $columns;
    protected $idColumnName;
    protected $aliveColumnName;
    protected $safeUrlColumnName;
    protected $relaciones;
    protected $ci;
    
    //flags:
    // l  list
    // g  get
    // i  insert
    // u  update
    
    /**
     * PrunusTable::__construct()
     * 
     * @param string $tableName Name of the thable
     * @param array $columns  Array of columns definition
     * @return
     */
    public function __construct($tableName, $columns, $idColumnName, $aliveColumnName, $safeUrlColumnName = null) {
      parent::__construct();
      $this->ci =& get_instance();
      $this->tableName   = $tableName;
      $this->columns     = $columns;
      $this->relaciones  = array();
      $this->idColumnName = $idColumnName;
      $this->aliveColumnName = $aliveColumnName;
      $this->safeUrlColumnName = $safeUrlColumnName;
    }
    
    public function getTableName()    { return ($this->tableName); }
    public function getIdColumn()     { return ($this->idColumnName); }
    public function getAliveColumn()  { return ($this->aliveColumnName); }
    public function getSafeUrlColumn(){ return ($this->safeUrlColumnName); }

    
    public function getRelaciones() {
      return ($this->relaciones);
    }
    public function getRelacion($name) {
      return ($this->relaciones[$name]);
    }

    public function addRelation($name, &$relation) {
      $this->relaciones[$name] = $relation;
    }
    
    /**
     * PrunusTable::countRecords()
     * 
     * @param integer|null $alive indicates if alive/non-alive records are counted. (null means don't filter by this value)
     * @param array|string|null $filters as string it's the whole where clause (literal). as an array it uses array's key for column name and array's value for the value used on where  
     * @param array|null $filters as string it's the whole where clause (literal). as an array it uses array's key for column name and array's value for the value used on where  
     * @return integer records matching the parameters
     */
    public function countRecords($alive = 1, $filters = null, $inlist = null) {
      $result = $this->_listRecords( TRUE, $alive, $filters, $inlist )->row_array();
      return (array_pop($result));
    }
    
    /**
     * PrunusTable::listRecords()
     * 
     * @param integer $alive optional integer indicating if alive/non-alive records are counted. (null means don't filter by this value)
     * @param array|string|null $filters as string it's the whole where clause (literal). as an array it uses array's key for column name (you can include here after the name, the comparison operator) and array's value for the value used on where clause
     * @param array|null $inlist array where every record name is the column name, and the value is an array of values to test
     * @param integer|null $offset starting record on paged query. to avoid paging, use null on this column
     * @param integer|null $limit if $offset is not null, this value contains the number of rows per page 
     * @param array|null $order array indicating how to sort results. array's key is the columns name, and array's value is the type of order (asc or desc)
     * @return CI_DB A class containing the get method results
     */
    public function listRecords($alive = 1, $filters = null, $inlist = null, $offset = null, $limit = null, $order = null) {
      return $this->_listRecords( FALSE, $alive, $filters, $inlist, $offset, $limit, $order );
    }
    
    /**
     * PrunusTable::_listRecords()
     * 
     * @param bool $count TRUE means the result of the query is the number of rows, otherwhise it returns the rows 
     * @param integer $alive optional integer indicating if alive/non-alive records are counted. (null means don't filter by this value)
     * @param array|string $filters as string it's the whole where clause (literal). as an array it uses array's key for column name and array's value for the value used on where
     * @param array|null $inlist array where every record name is the column name, and the value is an array of values to test
     * @param integer|null $offset starting record on paged query. to avoid paging, use null on this column
     * @param integer|null $limit if $offset is not null, this value contains the number of rows per page 
     * @param array|null $order array indicating how to sort results. array's key is the columns name, and array's value is the type of order (asc or desc)
     * @return CI_DB A class containing the get method results
     */
    private function _listRecords($count, $alive, $filters, $inlist, $offset = null, $limit = null, $order = null) {
      if ($count) {
        $this->ci->db->select('count(0)', FALSE);
      } else {
        foreach ($this->columns as $columnName=>$columnInfo) if (strpos($columnInfo['flags'],'l')!==FALSE) $this->ci->db->select($columnName);
      }
      if ($inlist !== null) foreach ($inlist as $column=>$list) $this->db->where_in($column, $list);
      if (is_array($filters)) foreach($filters as $column=>$value) $this->ci->db->where($column, $value);
      if (is_string($filters)) $this->ci->db->where($filters);
      if ($alive !== null) $this->ci->db->where($this->aliveColumnName, $alive);
      if ( ! $count) {
        if ($order !== null) foreach ($order as $column=>$value) $this->ci->db->order_by($column,$value);
        if ($offset !== null) $this->ci->db->limit($limit,$offset);
      }
      return ($this->ci->db->get($this->tableName));      
    }

    /**
     * PrunusTable::getRecord()
     * 
     * @param integer|string $id if integer it's the column's id. if string, it's column's safe url.
     * @return CI_DB A class containing the get method results
     */
    public function getRecord($id) {
      foreach ($this->columns as $columnName=>$columnInfo) if (strpos($columnInfo['flags'],'g')!==FALSE) $this->ci->db->select($columnName);
      if (is_numeric($id)) $this->ci->db->where($this->idColumnName, $id); else $this->ci->db->where($this->safeUrlColumnName, $id);
      $this->ci->db->limit(1);
      return ($this->ci->db->get($this->tableName));      
    }
    
    public function getInsertColumns() { //solo para ayudar en el insert
      $out = array();
      foreach ($this->columns as $columnName=>$columnInfo) if (strpos($columnInfo['flags'],'i')!==FALSE) $out[] = $columnName;
      return ($out);
    }
    public function getUpdateColumns() { //solo para ayudar en el insert
      $out = array();
      foreach ($this->columns as $columnName=>$columnInfo) if (strpos($columnInfo['flags'],'u')!==FALSE) $out[] = $columnName;
      return ($out);
    }
  
    /**
     * PrunusTable::insertRecord()
     * 
     * @param array|null $data array containing columns' values of the record to insert. array's key is the column's name and array's value is the column's value 
     * @param array|null $dataNonEscaped same as $data but column's value will not be escaped
     * @return integer id from the inserted record
     */
    public function insertRecord($data = null, $dataNonEscaped = null) {
      if ($data !== null) foreach($data as $column=>$value) $this->ci->db->set($column, $value);
      if ($dataNonEscaped !== null) foreach($dataNonEscaped as $column=>$value) $this->ci->db->set($column, $value, FALSE);
      foreach ($this->columns as $columnName=>$columnInfo) if (array_key_exists('value', $columnInfo)) $this->ci->db->set($columnName, $columnInfo['value']);
      $this->ci->db->insert($this->tableName);
      return ($this->ci->db->insert_id());    
    }
  
    /**
     * PrunusTable::updateRecord()
     * 
     * @param integer|string|null $id if integer it's the column id to update. if string, it's column safe url to update. if null this is a multiple update, use it with $filters to control the columns updated.
     * @param array|null $data array containing columns' values to update. array's key is the column's name and array's value is the column's value 
     * @param array|null $dataNonEscaped same as $data but column's value will not be escaped
     * @param array|string|null $filters as string it's the whole where clause (literal). as an array it uses array's key for column name and array's value for the value used on where
     * @param array|null $inlist array where on every record, it's name is a column name, and it's value is an array of values to test
     * @return
     */
    public function updateRecord($id, $data = null, $dataNonEscaped = null, $filters = null, $inlist = null) {
      if ($data !== null) foreach($data as $column=>$value) $this->ci->db->set($column, $value);
      if ($dataNonEscaped !== null) foreach($dataNonEscaped as $column=>$value) $this->ci->db->set($column, $value, FALSE);
      if ($inlist !== null) foreach ($inlist as $column=>$list) $this->db->where_in($column, $list);
      if (is_array($filters)) foreach($filters as $column=>$value) $this->ci->db->where($column, $value);
      if (is_string($filters)) $this->ci->db->where($filters);
      if ($id !== null) if (is_numeric($id)) $this->ci->db->where($this->idColumnName, $id); else $this->ci->db->where($this->safeUrlColumnName, $id);
      return ($this->ci->db->update($this->tableName));
    }
  
    /**
     * PrunusTable::deleteRecord()
     * Logical delete
     * 
     * @param integer|string|null $id if integer it's the column id to delete. if string, it's column safe url to delete. if null this is a multiple update, use it with $filters to control the columns deleted.
     * @param array|string|null $filters as string it's the whole where clause (literal). as an array it uses array's key for column name and array's value for the value used on where
     * @param array|null $inlist array where on every record, it's name is a column name, and it's value is an array of values to test
     * @return
     */
    public function deleteRecord($id, $filters = null, $inlist = null) {
      return ($this->updateRecord($id, array($this->aliveColumnName  => "0"), null, $filters, $inlist));
    }
  
    /**
     * PrunusTable::undeleteRecord()
     * Undelete a logical deleted record
     * 
     * @param integer|string|null $id if integer it's the column id to delete. if string, it's column safe url to delete. if null this is a multiple update, use it with $filters to control the columns deleted.
     * @param array|string|null $filters as string it's the whole where clause (literal). as an array it uses array's key for column name and array's value for the value used on where
     * @param array|null $inlist array where on every record, it's name is a column name, and it's value is an array of values to test
     * @return
     */
    public function undeleteRecord($id, $filters = null, $inlist = null) {
      return ($this->updateRecord($id, array($this->aliveColumnName  => "1"), null, $filters, $inlist));
    }
  
    /**
     * PrunusTable::killRecord()
     * Physicall delete
     * 
     * @param integer|string|null $id if integer it's the column id to delete. if string, it's column safe url to delete. if null this is a multiple update, use it with $filters to control the columns deleted.
     * @param array|string|null $filters as string it's the whole where clause (literal). as an array it uses array's key for column name and array's value for the value used on where
     * @return
     */
    public function killRecord($id, $filters = null) {
      if (is_numeric($id)) $this->ci->db->where($this->idColumnName, $id); else $this->ci->db->where($this->safeUrlColumnName, $id);
      if (is_array($filters)) foreach($filters as $column=>$value) $this->ci->db->where($column, $value);
      if (is_string($filters)) $this->ci->db->where($filters);
      return ($this->ci->db->delete($this->tableName));
    }
  
    /**
     * PrunusTable::truncateRecord()
     * Truncates whole table
     *  
     * @return
     */
    public function truncate() {
      return ($this->ci->db->truncate($this->tableName));
    }
  
  }
}
 
